home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / lynx-2.4 / WWW / Library / Implementation / SGML.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-28  |  16.6 KB  |  679 lines

  1. /*            General SGML Parser code        SGML.c
  2. **            ========================
  3. **
  4. **    This module implements an HTStream object. To parse an
  5. **    SGML file, create this object which is a parser. The object
  6. **    is (currently) created by being passed a DTD structure,
  7. **    and a target HTStructured oject at which to throw the parsed stuff.
  8. **    
  9. **     6 Feb 93  Binary seraches used. Intreface modified.
  10. */
  11. #include "HTUtils.h"
  12. #include "tcp.h"        /* For FROMASCII */
  13.  
  14. #include "SGML.h"
  15.  
  16. #include <ctype.h>
  17. /*#include <stdio.h> included in HTUtils.h -- FM */
  18. #include "HTChunk.h"
  19.  
  20. #include "LYLeaks.h"
  21.  
  22. #define INVALID (-1)
  23.  
  24. /*    The State (context) of the parser
  25. **
  26. **    This is passed with each call to make the parser reentrant
  27. **
  28. */
  29.  
  30. #define MAX_ATTRIBUTES 20    /* Max number of attributes per element */
  31.  
  32.     
  33. /*        Element Stack
  34. **        -------------
  35. **    This allows us to return down the stack reselcting styles.
  36. **    As we return, attribute values will be garbage in general.
  37. */
  38. typedef struct _HTElement HTElement;
  39. struct _HTElement {
  40.     HTElement *    next;    /* Previously nested element or 0 */
  41.     HTTag*        tag;    /* The tag at this level  */
  42. };
  43.  
  44.  
  45. /*    Internal Context Data Structure
  46. **    -------------------------------
  47. */
  48. struct _HTStream {
  49.  
  50.     CONST HTStreamClass *    isa;        /* inherited from HTStream */
  51.     
  52.     CONST SGML_dtd         *dtd;
  53.     HTStructuredClass    *actions;    /* target class  */
  54.     HTStructured    *target;    /* target object */
  55.  
  56.     HTTag         *current_tag;
  57.     int         current_attribute_number;
  58.     HTChunk        *string;
  59.     HTElement        *element_stack;
  60.     enum sgml_state { S_text, S_litteral, S_tag, S_tag_gap, 
  61.         S_attr, S_attr_gap, S_equals, S_value,
  62.         S_ero, S_cro,
  63.           S_squoted, S_dquoted, S_end, S_entity, S_junk_tag} state;
  64. #ifdef CALLERDATA          
  65.     void *        callerData;
  66. #endif
  67.     BOOL present[MAX_ATTRIBUTES];    /* Flags: attribute is present? */
  68.     char * value[MAX_ATTRIBUTES];    /* malloc'd strings or NULL if none */
  69. } ;
  70.  
  71.  
  72. #define PUTC(ch) ((*context->actions->put_character)(context->target, ch))
  73.  
  74.  
  75.  
  76. /*    Handle Attribute
  77. **    ----------------
  78. */
  79. /* PUBLIC CONST char * SGML_default = "";   ?? */
  80.  
  81. #ifdef __STDC__
  82. PRIVATE void handle_attribute_name(HTStream * context, const char * s)
  83. #else
  84. PRIVATE void handle_attribute_name(context, s)
  85.     HTStream * context;
  86.     char *s;
  87. #endif
  88. {
  89.  
  90.     HTTag * tag = context->current_tag;
  91.     attr* attributes = tag->attributes;
  92.  
  93.     int high, low, i, diff;        /* Binary search for attribute name */
  94.     for(low=0, high=tag->number_of_attributes;
  95.             high > low ;
  96.         diff < 0 ? (low = i+1) : (high = i) )  {
  97.     i = (low + (high-low)/2);
  98.     diff = strcasecomp(attributes[i].name, s);
  99.     if (diff==0) {            /* success: found it */
  100.             context->current_attribute_number = i;
  101.         context->present[i] = YES;
  102.         if (context->value[i]) {
  103.         free(context->value[i]);
  104.         context->value[i] = NULL;
  105.         }
  106.         return;
  107.     } /* if */
  108.     
  109.     } /* for */
  110.     
  111.     if (TRACE)
  112.     fprintf(stderr, "SGML: Unknown attribute %s for tag %s\n",
  113.         s, context->current_tag->name);
  114.     context->current_attribute_number = INVALID;    /* Invalid */
  115. }
  116.  
  117.  
  118. /*    Handle attribute value
  119. **    ----------------------
  120. */
  121. #ifdef __STDC__
  122. PRIVATE void handle_attribute_value(HTStream * context, const char * s)
  123. #else
  124. PRIVATE void handle_attribute_value(context, s)
  125.     HTStream * context;
  126.     char *s;
  127. #endif
  128. {
  129.     if (context->current_attribute_number != INVALID) {
  130.     StrAllocCopy(context->value[context->current_attribute_number], s);
  131.     } else {
  132.         if (TRACE) fprintf(stderr, "SGML: Attribute value %s ignored\n", s);
  133.     }
  134.     context->current_attribute_number = INVALID; /* can't have two assignments! */
  135. }
  136.  
  137.  
  138. /*    Handle entity
  139. **    -------------
  140. **
  141. ** On entry,
  142. **    s    contains the entity name zero terminated
  143. ** Bugs:
  144. **    If the entity name is unknown, the terminator is treated as
  145. **    a printable non-special character in all cases, even if it is '<'
  146. */
  147. #ifdef __STDC__
  148. PRIVATE void handle_entity(HTStream * context, char term)
  149. #else
  150. PRIVATE void handle_entity(context, term)
  151.     HTStream * context;
  152.     char term;
  153. #endif
  154. {
  155.  
  156.     CONST char ** entities = context->dtd->entity_names;
  157.     CONST char *s = context->string->data;
  158.     
  159.     int high, low, i, diff;
  160.     for(low=0, high = context->dtd->number_of_entities;
  161.             high > low ;
  162.         diff < 0 ? (low = i+1) : (high = i))   {  /* Binary serach */
  163.     i = (low + (high-low)/2);
  164.     diff = strcmp(entities[i], s);    /* Csse sensitive! */
  165.     if (diff==0) {            /* success: found it */
  166.         (*context->actions->put_entity)(context->target, i);
  167.         return;
  168.     }
  169.     }
  170.     /* If entity string not found, display as text */
  171.     if (TRACE)
  172.     fprintf(stderr, "SGML: Unknown entity %s\n", s); 
  173.     PUTC('&');
  174.     {
  175.     CONST char *p;
  176.     for (p=s; *p; p++) {
  177.         PUTC(*p);
  178.     }
  179.     }
  180.     PUTC(term);
  181. }
  182.  
  183.  
  184. /*    End element
  185. **    -----------
  186. */
  187. #ifdef __STDC__
  188. PRIVATE void end_element(HTStream * context, HTTag * old_tag)
  189. #else
  190. PRIVATE void end_element(context, old_tag)
  191.     HTTag * old_tag;
  192.     HTStream * context;
  193. #endif
  194. {
  195.     if (TRACE) fprintf(stderr, "SGML: End   </%s>\n", old_tag->name);
  196.     if (old_tag->contents == SGML_EMPTY) {
  197.         if (TRACE) fprintf(stderr,"SGML: Illegal end tag </%s> found.\n",
  198.         old_tag->name);
  199.     return;
  200.     }
  201.     while (context->element_stack)     {/* Loop is error path only */
  202.     HTElement * N = context->element_stack;
  203.     HTTag * t = N->tag;
  204.     
  205.     if (old_tag != t) {        /* Mismatch: syntax error */
  206.         if (context->element_stack->next) {    /* This is not the last level */
  207.         if (TRACE) fprintf(stderr,
  208.             "SGML: Found </%s> when expecting </%s>. </%s> assumed.\n",
  209.             old_tag->name, t->name, t->name);
  210.         } else {            /* last level */
  211.         if (TRACE) fprintf(stderr,
  212.                 "SGML: Found </%s> when expecting </%s>. </%s> Ignored.\n",
  213.             old_tag->name, t->name, old_tag->name);
  214.             return;            /* Ignore */
  215.         }
  216.     }
  217.     
  218.     context->element_stack = N->next;        /* Remove from stack */
  219.     free(N);
  220.     (*context->actions->end_element)(context->target,
  221.          t - context->dtd->tags);
  222.     if (old_tag == t) return;  /* Correct sequence */
  223.     
  224.     /* Syntax error path only */
  225.     
  226.     }
  227.     if (TRACE) fprintf(stderr,
  228.     "SGML: Extra end tag </%s> found and ignored.\n", old_tag->name);
  229. }
  230.  
  231.  
  232. /*    Start a element
  233. */
  234. #ifdef __STDC__
  235. PRIVATE void start_element(HTStream * context)
  236. #else
  237. PRIVATE void start_element(context)
  238.     HTStream * context;
  239. #endif
  240. {
  241.     HTTag * new_tag = context->current_tag;
  242.     
  243.     if (TRACE) fprintf(stderr, "SGML: Start <%s>\n", new_tag->name);
  244.     (*context->actions->start_element)(
  245.         context->target,
  246.     new_tag - context->dtd->tags,
  247.     context->present,
  248.     (CONST char**) context->value);  /* coerce type for think c */
  249.     if (new_tag->contents != SGML_EMPTY) {        /* i.e. tag not empty */
  250.     HTElement * N = (HTElement *)malloc(sizeof(HTElement));
  251.         if (N == NULL) outofmem(__FILE__, "start_element");
  252.     N->next = context->element_stack;
  253.     N->tag = new_tag;
  254.     context->element_stack = N;
  255.     }
  256. }
  257.  
  258.  
  259. /*        Find Tag in DTD tag list
  260. **        ------------------------
  261. **
  262. ** On entry,
  263. **    dtd    points to dtd structire including valid tag list
  264. **    string    points to name of tag in question
  265. **
  266. ** On exit,
  267. **    returns:
  268. **        NULL        tag not found
  269. **        else        address of tag structure in dtd
  270. */
  271. PUBLIC HTTag * SGMLFindTag ARGS2(CONST SGML_dtd*, dtd, CONST char *, string)
  272. {
  273.     int high, low, i, diff;
  274.     for(low=0, high=dtd->number_of_tags;
  275.             high > low ;
  276.         diff < 0 ? (low = i+1) : (high = i))   {  /* Binary serach */
  277.     i = (low + (high-low)/2);
  278.     diff = strcasecomp(dtd->tags[i].name, string);    /* Case insensitive */
  279.     if (diff==0) {            /* success: found it */
  280.         return &dtd->tags[i];
  281.     }
  282.     }
  283.     return NULL;
  284. }
  285.  
  286. /*________________________________________________________________________
  287. **            Public Methods
  288. */
  289.  
  290.  
  291. /*    Could check that we are back to bottom of stack! @@  */
  292.  
  293. PUBLIC void SGML_free  ARGS1(HTStream *, context)
  294. {
  295.     int i;
  296.  
  297.     (*context->actions->_free)(context->target);
  298.     HTChunkFree(context->string);
  299.  
  300.     /* free strings */
  301.     for(i=0; i<MAX_ATTRIBUTES; i++) 
  302.     if(context->value[i])
  303.        free(context->value[i]);
  304.  
  305.     free(context);
  306. }
  307.  
  308. PUBLIC void SGML_abort  ARGS2(HTStream *, context, HTError, e)
  309. {
  310.     (*context->actions->_abort)(context->target, e);
  311.     HTChunkFree(context->string);
  312.     free(context);
  313. }
  314.  
  315.  
  316. /*    Read and write user callback handle
  317. **    -----------------------------------
  318. **
  319. **   The callbacks from the SGML parser have an SGML context parameter.
  320. **   These calls allow the caller to associate his own context with a
  321. **   particular SGML context.
  322. */
  323.  
  324. #ifdef CALLERDATA          
  325. PUBLIC void* SGML_callerData ARGS1(HTStream *, context)
  326. {
  327.     return context->callerData;
  328. }
  329.  
  330. PUBLIC void SGML_setCallerData ARGS2(HTStream *, context, void*, data)
  331. {
  332.     context->callerData = data;
  333. }
  334. #endif
  335.  
  336. PUBLIC void SGML_character ARGS2(HTStream *, context, char,c)
  337.  
  338. {
  339.     CONST SGML_dtd    *dtd    =    context->dtd;
  340.     HTChunk    *string =     context->string;
  341.  
  342.     switch(context->state) {
  343.     case S_text:
  344.     if (c=='&' && (!context->element_stack || (
  345.                  context->element_stack->tag  &&
  346.                  ( context->element_stack->tag->contents == SGML_MIXED
  347.                || context->element_stack->tag->contents ==
  348.                                    SGML_RCDATA)
  349.             ))) {
  350.         /*    Setting up for entity, without the leading '&'
  351.          */
  352.         string->size = 0;
  353.         context->state = S_ero;
  354.         
  355.     } else if (c=='<') {
  356.         string->size = 0;
  357.         context->state = (context->element_stack &&
  358.                 context->element_stack->tag  &&
  359.                 context->element_stack->tag->contents == SGML_LITTERAL) ?
  360.                     S_litteral : S_tag;
  361.     } else PUTC(c);
  362.     break;
  363.  
  364. /*    In litteral mode, waits only for specific end tag!
  365. **    Only foir compatibility with old servers.
  366. */
  367.     case S_litteral :
  368.     HTChunkPutc(string, c);
  369.     if ( TOUPPER(c) != ((string->size ==1) ? '/'
  370.         : context->element_stack->tag->name[string->size-2])) {
  371.         int i;
  372.         
  373.         /*    If complete match, end litteral */
  374.         if ((c=='>') && (!context->element_stack->tag->name[string->size-2])) {
  375.         end_element(context, context->element_stack->tag);
  376.         string->size = 0;
  377.         context->current_attribute_number = INVALID;
  378.         context->state = S_text;
  379.         break;
  380.         }        /* If Mismatch: recover string. */
  381.         PUTC( '<');
  382.         for (i=0; i<string->size; i++)    /* recover */
  383.            PUTC(
  384.                                  string->data[i]);
  385.         context->state = S_text;    
  386.     }
  387.     
  388.         break;
  389.  
  390. /*    Character reference or Entity
  391. */
  392.    case S_ero:
  393.        if (c=='#') {
  394.         context->state = S_cro;  /*   &# is Char Ref Open */ 
  395.         break;
  396.     }
  397.     context->state = S_entity;    /* Fall through! */
  398.     
  399. /*    Handle Entities
  400. */
  401.     case S_entity:
  402.     if (isalnum(c))
  403.         HTChunkPutc(string, c);
  404.     else {
  405.         HTChunkTerminate(string);
  406.         handle_entity(context, c);
  407.         context->state = S_text;
  408.     }
  409.     break;
  410.  
  411. /*    Character reference
  412. */
  413.     case S_cro:
  414.     if (isalnum(c))
  415.         HTChunkPutc(string, c);    /* accumulate a character NUMBER */
  416.     else {
  417.         int value;
  418.         HTChunkTerminate(string);
  419.         if (sscanf(string->data, "%d", &value)==1)
  420.             PUTC(FROMASCII((char)value));
  421.         context->state = S_text;
  422.     }
  423.     break;
  424.  
  425. /*        Tag
  426. */        
  427.     case S_tag:                /* new tag */
  428.     if (isalnum(c))
  429.         HTChunkPutc(string, c);
  430.     else {                /* End of tag name */
  431.         HTTag * t;
  432.         if (c=='/') {
  433.         if (TRACE) if (string->size!=0)
  434.             fprintf(stderr,"SGML:  `<%s/' found!\n", string->data);
  435.         context->state = S_end;
  436.         break;
  437.         }
  438.         HTChunkTerminate(string) ;
  439.  
  440.         t = SGMLFindTag(dtd, string->data);
  441.         if (!t) {
  442.         if(TRACE) fprintf(stderr, "SGML: *** Unknown element %s\n",
  443.             string->data);
  444.         context->state = (c=='>') ? S_text : S_junk_tag;
  445.         break;
  446.         }
  447.         context->current_tag = t;
  448.         
  449.         /*  Clear out attributes
  450.         */
  451.         
  452.         {
  453.             int i;
  454.             for (i=0; i< context->current_tag->number_of_attributes; i++)
  455.                 context->present[i] = NO;
  456.         }
  457.         string->size = 0;
  458.         context->current_attribute_number = INVALID;
  459.         
  460.         if (c=='>') {
  461.         if (context->current_tag->name) start_element(context);
  462.         context->state = S_text;
  463.         } else {
  464.             context->state = S_tag_gap;
  465.         }
  466.     }
  467.     break;
  468.  
  469.         
  470.     case S_tag_gap:        /* Expecting attribute or > */
  471.     if (WHITE(c)) break;    /* Gap between attributes */
  472.     if (c=='>') {        /* End of tag */
  473.         if (context->current_tag->name) start_element(context);
  474.         context->state = S_text;
  475.         break;
  476.     }
  477.     HTChunkPutc(string, c);
  478.     context->state = S_attr;        /* Get attribute */
  479.     break;
  480.     
  481.                    /* accumulating value */
  482.     case S_attr:
  483.     if (WHITE(c) || (c=='>') || (c=='=')) {        /* End of word */
  484.         HTChunkTerminate(string) ;
  485.         handle_attribute_name(context, string->data);
  486.         string->size = 0;
  487.         if (c=='>') {        /* End of tag */
  488.         if (context->current_tag->name) start_element(context);
  489.         context->state = S_text;
  490.         break;
  491.         }
  492.         context->state = (c=='=' ?  S_equals: S_attr_gap);
  493.     } else {
  494.         HTChunkPutc(string, c);
  495.     }
  496.     break;
  497.         
  498.     case S_attr_gap:        /* Expecting attribute or = or > */
  499.     if (WHITE(c)) break;    /* Gap after attribute */
  500.     if (c=='>') {        /* End of tag */
  501.         if (context->current_tag->name) start_element(context);
  502.         context->state = S_text;
  503.         break;
  504.     } else if (c=='=') {
  505.         context->state = S_equals;
  506.         break;
  507.     }
  508.     HTChunkPutc(string, c);
  509.     context->state = S_attr;        /* Get next attribute */
  510.     break;
  511.     
  512.     case S_equals:            /* After attr = */ 
  513.     if (WHITE(c)) break;    /* Before attribute value */
  514.     if (c=='>') {        /* End of tag */
  515.         if (TRACE) fprintf(stderr, "SGML: found = but no value\n");
  516.         if (context->current_tag->name) start_element(context);
  517.         context->state = S_text;
  518.         break;
  519.         
  520.     } else if (c=='\'') {
  521.         context->state = S_squoted;
  522.         break;
  523.  
  524.     } else if (c=='"') {
  525.         context->state = S_dquoted;
  526.         break;
  527.     }
  528.     HTChunkPutc(string, c);
  529.     context->state = S_value;
  530.     break;
  531.     
  532.     case S_value:
  533.     if (WHITE(c) || (c=='>')) {        /* End of word */
  534.         HTChunkTerminate(string) ;
  535.         handle_attribute_value(context, string->data);
  536.         string->size = 0;
  537.         if (c=='>') {        /* End of tag */
  538.         if (context->current_tag->name) start_element(context);
  539.         context->state = S_text;
  540.         break;
  541.         }
  542.         else context->state = S_tag_gap;
  543.     } else {
  544.         HTChunkPutc(string, c);
  545.     }
  546.     break;
  547.         
  548.     case S_squoted:        /* Quoted attribute value */
  549.     if (c=='\'') {        /* End of attribute value */
  550.         HTChunkTerminate(string) ;
  551.         handle_attribute_value(context, string->data);
  552.         string->size = 0;
  553.         context->state = S_tag_gap;
  554.     } else {
  555.         HTChunkPutc(string, c);
  556.     }
  557.     break;
  558.     
  559.     case S_dquoted:        /* Quoted attribute value */
  560.     if (c=='"') {        /* End of attribute value */
  561.         HTChunkTerminate(string) ;
  562.         handle_attribute_value(context, string->data);
  563.         string->size = 0;
  564.         context->state = S_tag_gap;
  565.     } else {
  566.         HTChunkPutc(string, c);
  567.     }
  568.     break;
  569.     
  570.     case S_end:                    /* </ */
  571.     if (isalnum(c))
  572.         HTChunkPutc(string, c);
  573.     else {                /* End of end tag name */
  574.         HTTag * t=0;
  575.         HTChunkTerminate(string) ;
  576.         if (!*string->data)    {    /* Empty end tag */
  577.         if(context->element_stack)
  578.                 t = context->element_stack->tag;
  579.         } else {
  580.         t = SGMLFindTag(dtd, string->data);
  581.         }
  582.         if (!t) {
  583.         if(TRACE) fprintf(stderr,
  584.             "Unknown end tag </%s>\n", string->data); 
  585.         } else {
  586.             context->current_tag = t;
  587.         end_element( context, context->current_tag);
  588.         }
  589.  
  590.         string->size = 0;
  591.         context->current_attribute_number = INVALID;
  592.         if (c!='>') {
  593.         if (TRACE && !WHITE(c))
  594.             fprintf(stderr,"SGML:  `</%s%c' found!\n",
  595.                 string->data, c);
  596.         context->state = S_junk_tag;
  597.         } else {
  598.             context->state = S_text;
  599.         }
  600.     }
  601.     break;
  602.  
  603.         
  604.     case S_junk_tag:
  605.     if (c=='>') {
  606.         context->state = S_text;
  607.     }
  608.     
  609.     } /* switch on context->state */
  610.  
  611. }  /* SGML_character */
  612.  
  613.  
  614. PUBLIC void SGML_string ARGS2(HTStream *, context, CONST char*, str)
  615. {
  616.     CONST char *p;
  617.     for(p=str; *p; p++)
  618.         SGML_character(context, *p);
  619. }
  620.  
  621.  
  622. PUBLIC void SGML_write ARGS3(HTStream *, context, CONST char*, str, int, l)
  623. {
  624.     CONST char *p;
  625.     CONST char *e = str+l;
  626.     for(p=str; p<e; p++)
  627.         SGML_character(context, *p);
  628. }
  629.  
  630. /*_______________________________________________________________________
  631. */
  632.  
  633. /*    Structured Object Class
  634. **    -----------------------
  635. */
  636. PUBLIC CONST HTStreamClass SGMLParser = 
  637. {        
  638.     "SGMLParser",
  639.     SGML_free,
  640.     SGML_abort,
  641.     SGML_character, 
  642.     SGML_string,
  643.     SGML_write,
  644. }; 
  645.  
  646. /*    Create SGML Engine
  647. **    ------------------
  648. **
  649. ** On entry,
  650. **    dtd        represents the DTD, along with
  651. **    actions        is the sink for the data as a set of routines.
  652. **
  653. */
  654.  
  655. PUBLIC HTStream* SGML_new  ARGS2(
  656.     CONST SGML_dtd *,    dtd,
  657.     HTStructured *,        target)
  658. {
  659.     int i;
  660.     HTStream* context = (HTStream *) malloc(sizeof(*context));
  661.     if (!context) outofmem(__FILE__, "SGML_begin");
  662.  
  663.     context->isa = &SGMLParser;
  664.     context->string = HTChunkCreate(128);    /* Grow by this much */
  665.     context->dtd = dtd;
  666.     context->target = target;
  667.     context->actions = (HTStructuredClass*)(((HTStream*)target)->isa);
  668.                         /* Ugh: no OO */
  669.     context->state = S_text;
  670.     context->element_stack = 0;            /* empty */
  671. #ifdef CALLERDATA          
  672.     context->callerData = (void*) callerData;
  673. #endif    
  674.     for(i=0; i<MAX_ATTRIBUTES; i++) context->value[i] = 0;
  675.  
  676.     return context;
  677. }
  678.  
  679.